import os from "os";
import fs from "fs";
import path from "path";
import http from "http";

import Koa from "koa";
import { pick, merge } from "lodash";
import { ApolloServer, PubSub } from "apollo-server-koa";
import { typeDefs as scalar_typeDefs, resolvers as scalar_resolvers } from "graphql-scalars";

import { Resolvers, SubscriptionResolvers } from "./generated/graphql";
import { portal } from "./resolvers/portal";
import { open_platform } from "./resolvers/open_platform";

// Construct a schema, using GraphQL schema language
const typeDefs = [
  // ...scalar_typeDefs,
  ...fs.readdirSync(path.join(__dirname, "../gqls/schemas")).map(it =>
    fs.readFileSync(path.join(__dirname, "../gqls/schemas", it), {
      encoding: "utf8",
    }),
  ),
];

const pubsub = new PubSub();

let count = 0;
// Provide resolver functions for your schema fields
const resolvers: Resolvers = {
  // ...scalar_resolvers,
  ...pick(scalar_resolvers, "Date", "JSONObject", "Void"),
  Query: {
    count: async (_, args, ctx, info) => {
      return count;
    },
  },
  Mutation: {
    add_count: async (_, args, ctx, info) => {
      count += args.num;
      pubsub.publish("on_new_count", count);
      return count;
    },
  },
  Subscription: {
    on_new_count: {
      // subscribe: withFilter(
      //   () => pubsub.asyncIterator("on_new_count"),
      //   (payload, args) => {
      //     return true;
      //   },
      // ),
      subscribe: async function* (_, args, ctx, info) {
        const ai = pubsub.asyncIterator<number>("on_new_count");
        for await (const iterator of (ai as unknown) as number[]) {
          if (iterator % 2 === 0) {
            yield iterator;
          }
        }
      },
      resolve: (_: any) => _,
    },
  },
};
merge(resolvers, portal, open_platform);

const server = new ApolloServer({
  typeDefs,
  resolvers: resolvers as any,
  subscriptions: {
    // 这是不需要的, 因为有 keepAlive
    // onConnect: (connparam, ws, ctx) => {
    //   console.log('onConnect');
    //   const interval: any = setInterval(() => {
    //     if (ws.CLOSED) {
    //       console.log(ws.CLOSED, 'onConnect closed');
    //       return clearInterval(interval);
    //     }
    //     ws.send('0');
    //   }, 1000);
    // },
    keepAlive: 1000,
  },
});

const app = new Koa();
server.applyMiddleware({ app });
// alternatively you can get a composed middleware from the apollo server
// app.use(server.getMiddleware());

const httpServer = http.createServer(app.callback());
server.installSubscriptionHandlers(httpServer);

const PORT = 4000;
httpServer.listen(PORT, "0.0.0.0", () => {
  const getNetworkAddress = () => {
    const interfaces = os.networkInterfaces();
    for (const network of Object.entries(interfaces)) {
      for (const inter of network[1]!) {
        const { address, family, internal } = inter;
        if (family === "IPv4" && !internal) {
          return address;
        }
      }
    }
  };
  const address = getNetworkAddress()!;
  console.log(`🚀 Server ready at http://${address}:${PORT}${server.graphqlPath}`);
  console.log(`🚀 Subscriptions ready at ws://${address}:${PORT}${server.subscriptionsPath}`);
});

process.on("SIGUSR2", () => {
  httpServer.close(() => {
    process.kill(process.pid, "SIGUSR2");
  });
});
